package com.tedu.api.io;

import org.junit.Test;

import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * IO流：传输数据
 * File文件、文件夹：无法读写数据
 * RandomAccessFile：读写按照字节，读写类不明确，靠构造器传入第二个参数确定
 * Java提出一套 标准     IO---方向区别
 * ①方向区别：
 * 输入流---（读：针对文件，，文件往程序中走，，文件”相对于程序“）
 * 输出流---（写：针对文件，文件写出，，文件“相对于程序”）
 * 输入流和输出流创建方式？
 * 输入流 InputStream（抽象类）--顶级父类---读操作
 * 输出类 OutputStream（抽象类）---顶级父类--写操作
 * 以上两个流定义所有流具备读写操作，，本身无法实例化
 * ②按照功能进行区别：
 * 低级流：专门搬运数据的流
 * 输入流 FileInputStream 文件输入流：读操作   ---InputStream（抽象类）
 * * 输出类 FileOutputStream    文件输出流：写操作    ---OutputStream（抽象类）
 * 功能：建立在程序与数据源之间的“管道”，所有数据传输通过低级流【节点流】
 * * 高级流：不能独立存在的流【叫做处理流】
 * 处理低级流的数据，并且可以被其他的流所处理
 * 理解：处理搬运数据的流【高级流种类比较多】
 * -----流：明确功能，创建对象：直接使用低级流--高级流创建
 * 新项目：聊天室的  Version 1 IO流：针对传字符串
 */
public class IOAPI {
    @Test
    public void testIO() throws IOException {
        //RandomAccessFile a=
        // new RandomAccessFile("fos.text","rw");
        //创建标准IO流，方向划分：输入流于输出流----功能：低级流【节点流】
        //需求：从项目中写出一个文件”fos.txt“   低级流：FileOutputStream
        //创建文件输出流   构造器（”相对路径+文件全称“）
        //抛出异常FileNotFoundException：没有文件异常！
        //原因：路径或者文件名写错
        FileOutputStream fos = new FileOutputStream("fos.txt");
        /**
         * 输出一句话：
         * write(字符串：字符数组)   返回值：void
         * 作用：写出一个文件内容：字节数组
         * 抛出异常  IOException  IO流异常：写出失败
         */
        //String 字符串的方法：getBytes()  可以将字符串转换成字符数组 char[]
        fos.write("我爱闫佳赛".getBytes());
        //标准IO流必须关闭
        fos.close();//释放流：避免内存占用
    }

    /**
     * FileOutputStream实现类----构造器使用方法
     * new FileOutputStream（String path,boolean append）
     * 通过第二个参数，确定·数据是否追加，true代表追加，反之
     * new FileOutputStream（File file,boolean append）
     */
    @Test
    public void testIOA() throws IOException {
        //当前API项目 下添加fos.txt--增加功能：追加效果
        //相对路径  ./fos.txt   直接写文件|不写./    都是当前目录下
        FileOutputStream fos = new FileOutputStream("fos.txt", true);
        String str = "是未来~";
        fos.write(str.getBytes());
        //关闭流
        fos.close();
    }

    /**
     * 低级流
     * 文件输入流
     * FileInputStream
     * read(字节数组)     返回值：int
     * 作用：读取到一个字节的信息
     */
    @Test
    public void testInputStream() throws IOException {
        //创建文件输入流----读入
        FileInputStream fis = new FileInputStream("fos.txt");
        //10KB 字节数组
        byte[] bytes = new byte[1024 * 10];
        //10KB  读  数据  【不想看到整数值】
        int len = fis.read(bytes);   //len  返回  -1   文件读取完毕
        //String字符串类--提供构造器方法：按照字节数组量：输出字符串
        //String构造器new String（字节数组，开始读取文件，结束读取文件）;
        //将【二进制】整数  转换为  字符串
        String str = new String(bytes, 0, len);  //循环
        //一次性将给定的字节数组【10KB】写入到文件当中，文件从零开始写入，到-1结束，写入完毕
        System.out.println("打印读：" + str);
        fis.close();
    }

    //需求：【标准IO流】重构RandomAccessFile类---复制视频方法
    @Test
    public void testWork() throws IOException {
        //1.创建输入流---读---被复制文件【源文件】
        FileInputStream fis = new FileInputStream("复制1.mp4");
        //2.创建输出流---写---复制品
        FileOutputStream fos = new FileOutputStream("视频.mp4");
        //3.用于保存读取到的字节量
        int len = -1; //①保存读取的数据值【可以被覆盖】  ②-1可以代表文件结尾
        //6.视频空间大，解决方案：10KB，字节数组：设定保存值10KB
        byte[] bytes = new byte[1024 * 10];
        long start = System.currentTimeMillis();
        while ((len = fis.read(bytes)) != -1) {
            fos.write(bytes, 0, len);
        }
        long end = System.currentTimeMillis();
        //4.一直读取文件，保存在len中，如果结果不等于-1，一直读
        //4.1 将读取到的文件写出
        //5.关闭流
        System.out.println("测试输出时间：" + (end - start) + "毫秒");
        fis.close();
        fos.close();
    }

    /**
     * 高级流【处理流】：处理低级流以及其他高级流的”一种“流
     * 高级流不能独立存在：一定处理低级流
     * BufferedInputStream -----高级流：快速读   缓存输入流
     * BufferedOutputStream-----高级流：快速写    缓存输出流
     * 需求：视频 使用IO流【低级流+高级流】---实现快速视频快速复制
     */
    @Test
    public void testCV() throws IOException {
        //创建低级流：输出流：高级流、输出流：高级流
        FileInputStream fis = new FileInputStream("复制1.mp4");
        BufferedInputStream bis = new BufferedInputStream(fis);

        FileOutputStream fos = new FileOutputStream("缓冲文件.mp4");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //定义整数变量保存每次读取的字节量
        int len = -1;
        //将高级流：缓冲输入流--读取的数据--保存给交len，结果不等于-1
        //(len=bis.read())!=-1
        long start = System.currentTimeMillis();
        while ((len = bis.read()) != -1) {
            //write()  写出【缓冲字符输出流：自动产生合理字节数组】-->写出
            bos.write(len);
        }
        long end = System.currentTimeMillis();
        System.out.println("缓冲流花费时间：" + (end - start));
        //关闭流
        bis.close();
       fos.close();
    }

    /**
     * 【高级流：处理流】
     * BufferedOutputStream  缓冲输出流
     * 需求：写出一个文件：bos.txt，，写出：一段文本内容
     * 所有的流：全部都是正确的写法IO流异常最多，采取捕获异常并做出处理
     */
    @Test
    public void testBOS() throws IOException {
        //创建低级流--写出bos.txt
        FileOutputStream fos = null;
        //创建高级流---写出 一段文本内容【缓冲区：字节数组】
        BufferedOutputStream bos = null;
        try {
            fos = new FileOutputStream("bos.txt");
            bos = new BufferedOutputStream(fos);
            String str = "50块钱再好看也不如100块钱招人稀罕";
            //write(byte[] b)   作用：写出字节数组
            bos.write(str.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            /**
             * str数据量小      缓冲区大         强制送出
             * 45人           车100
             * flush()
             * 作用：可以强制将内部缓冲区一次性读写
             * 缺点：频繁使用flush降低我们的读写
             */
            //bos.flush();
            if ( bos!= null)
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

    /**
     * 标准IO  必须与   异常  存在     ----传输数据保障数据安全【处理异常】
     * 异常重点：1.认识代码中抛出异常，，异常产生原因
     * 2.异常处理--【快捷键：ctrl+alt+T】
     * 语法：try{
     * 怀疑可能有错误或者已经提示异常
     * }catch（小异常名字 e）{
     * e.printStackTrance();       //打印异常信息----通过异常信息解决问题
     * }catch(中异常名 e){  catch可以捕获很多异常：小到大
     * e.printStackTrance();
     * }
     * finally{
     * 一定执行代码块
     * }
     * --------------------------------------------------------------
     *    异常大小问题：
     *    IO流：FileNotFoundException>IOException
     *         Exception顶级父类
     *---------------------------------------------------------------
     * 低级流：FileOutputStream 文件输出流----写出
     *        FileInputStream  文件输入流----读入
     * 高级流：BufferedOutputStream  缓冲输出流-----写出----功能：缓冲区
     *        BufferedInputStream     缓冲输出流----读入-----功能：缓冲区
     *
     *
     * final与finally的区别？
     * final关键字：修饰 变量 方法 类---->变量成常量
     * 方法不能重写
     * 类不能被继承
     * finally关键字：异常处理机制中：捕获异常
     * finally在捕获异常中，可以写，也可以不写
     * 所有异常都不符合，一定会执行的语法块
     * 通常使用在IO流中：关闭流为主！  关闭高级流-->间接关闭低级流
     * <p>
     * 需求：BOS高级流----重新结合异常----标准IO流
     */
    @Test
    public void testBOS1() throws IOException {

        //1.需要将流置空：置空--①覆盖，不用多次创建对象！ ②关闭流
        FileOutputStream fos = null;     //低级流---传输数据
        BufferedOutputStream bos = null;   //高级流-----处理低级流：缓冲输出流
        //2.捕获异常操作:快捷键  alt+ctrl+T：怀疑IO流代码---->捕获异常
        //2.1创建输出流
        try {
            fos = new FileOutputStream("ab3"+File.pathSeparator+"BOS标准流的写法.txt");
            bos = new BufferedOutputStream(fos);
            //3.高级流：缓冲输出流---功能：缓冲区【字节数组】 写出数据
            bos.write("标准流的写法".getBytes());
            //4.缓冲区：数据量比较小---采用flush()   数据一次性写出
            bos.flush();
        } catch (FileNotFoundException e) {//文件不存在异常，，路径写错，，文件不存在
            e.printStackTrace();    //打印异常信息
            System.out.println("【小】文件不存在异常");
        } catch (IOException e) {   //IO流最大的异常：小的异常不处理
            e.printStackTrace();
            System.out.println("【中】IO流异常");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("【大】异常");
        } finally {
            //一定执行代码块：IO流关闭---高级流---关闭高级流--低级流关闭【捕获异常】
            //低级流----关闭低级流【捕获异常】
            //5.先判断，高级流---是否为空【有没有高级流对象】---有高级流对象【不为null，必须关闭】
            //                                           反之【为null，不必关闭】
            if (bos != null)
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }
}